/**
 * 
 */
package cz.cuni.mff.abacs.burglar.logics.planning;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.List;

import cz.cuni.mff.abacs.burglar.logics.objects.BaseObject;
import cz.cuni.mff.abacs.burglar.logics.objects.Room;
import cz.cuni.mff.abacs.burglar.logics.objects.agents.Agent;
import cz.cuni.mff.abacs.burglar.logics.objects.items.Item;
import cz.cuni.mff.abacs.burglar.logics.objects.positions.Position;


/**
 * 
 * Static class.
 * 
 * @author abacs
 *
 */
public abstract class PDDLBase {
	
	
	protected static final String STR_TYPE_ROOM = "t_room";
	
	protected static final String STR_TYPE_CAMERA = "t_camera";
	protected static final String STR_TYPE_CONTAINER = "t_container";
	protected static final String STR_TYPE_DOOR = "t_door";
	protected static final String STR_TYPE_FLOOR = "t_floor";
	protected static final String STR_TYPE_PHONE = "t_phone";
	protected static final String STR_TYPE_SWITCH = "t_switch";
	protected static final String STR_TYPE_VENDER = "t_vender";
	
	protected static final String STR_TYPE_KEY = "t_key";
//	protected static final String STR_TYPE_LADDER = "t_ladder";
	protected static final String STR_TYPE_TREASURE = "t_treasure";
	
	protected static final String STR_TYPE_BURGLAR = "t_burglar";
	protected static final String STR_TYPE_DOG = "t_dog";
	protected static final String STR_TYPE_GUARD = "t_guard";
	
	protected static final String STR_DUMMY = "dummy";
	
	protected static final String STR_TAG_OBJECTS = "\t(:objects \n";
	protected static final String STR_TAG_INIT = "\t(:init\n";
	protected static final String STR_TAG_END = "\t)\n";
	
	protected static final String STR_TAG_POSITION_IN = "\t\t(position_in ";
	protected static final String STR_TAG_CLOSED = "\t\t(closed ";
	protected static final String STR_TAG_LOCKED = "\t\t(locked ";
	protected static final String STR_TAG_NEEDS_KEY = "\t\t(needs_key ";
	protected static final String STR_TAG_CONTAINS = "\t\t(contains ";
	protected static final String STR_TAG_DOOR_CONNECTS = "\t\t(door_connects ";
	protected static final String STR_TAG_AGENT_NEXT_TO = "\t\t(agent_next_to ";
	protected static final String STR_TAG_AGENT_IN = "\t\t(agent_in ";
	protected static final String STR_TAG_VISITED = "\t\t(visited ";
	protected static final String STR_TAG_END_ = ")\n";
	protected static final String STR_TAG_END__ = ")";
	
	protected static final String STR_TAG_NOT = "(not ";
	
	
	// -------------------------------------------------------------------------
	
	
	/**
	 * Generates the position declarations for the objects tag.
	 * 
	 * @param objs positions as a list
	 * @param appendDummyFloor should append a dummy floor position?
	 * @return string of position object declarations by types.
	 */
	protected static String generatePositionList(
			List<Position> objs,
			boolean appendDummyFloor
	) {
		StringBuilder ret = new StringBuilder();
		
		StringBuilder cameras    = new StringBuilder();
		StringBuilder containers = new StringBuilder();
		StringBuilder doors      = new StringBuilder();
		StringBuilder floors     = new StringBuilder();
		StringBuilder phones     = new StringBuilder();
		StringBuilder switches   = new StringBuilder();
		StringBuilder venders    = new StringBuilder();
		
		for(Position obj : objs){
			String nameStr = generateObjectName(obj);
			switch(obj.getType()){
			case CAMERA:
				cameras.append(' ');
				cameras.append(nameStr);
				break;
			case CONTAINER:
				containers.append(' ');
				containers.append(nameStr);
				break;
			case DOOR:
				doors.append(' ');
				doors.append(nameStr);
				break;
			case FLOOR:
				floors.append(' ');
				floors.append(nameStr);
				break;
			case PHONE:
				phones.append(' ');
				phones.append(nameStr);
				break;
			case SWITCH:
				switches.append(' ');
				switches.append(nameStr);
				break;
			case VENDER:
				venders.append(' ');
				venders.append(nameStr);
				break;
			}
		}
		
		if(appendDummyFloor){
			floors.append(' ');
			floors.append(generateDummyName(STR_TYPE_FLOOR));
		}
		
		ret.append(generateDeclarationLine(cameras.toString(), STR_TYPE_CAMERA));
		ret.append(generateDeclarationLine(containers.toString(), STR_TYPE_CONTAINER));
		ret.append(generateDeclarationLine(doors.toString(), STR_TYPE_DOOR));
		ret.append(generateDeclarationLine(floors.toString(), STR_TYPE_FLOOR));
		ret.append(generateDeclarationLine(phones.toString(), STR_TYPE_PHONE));
		ret.append(generateDeclarationLine(switches.toString(), STR_TYPE_SWITCH));
		ret.append(generateDeclarationLine(venders.toString(), STR_TYPE_VENDER));
		
		return ret.toString();
	}
	
	
	/**
	 * Generates the position declarations for the objects tag.
	 * Only doors and floors are expected.
	 * 
	 * @param objs positions as a list
	 * @return string of position object declarations by types.
	 */
	protected static String generateSimplifiedPositionList(
			List<Position> objs
	) {
		StringBuilder ret = new StringBuilder();
		
		StringBuilder doors  = new StringBuilder();
		StringBuilder floors = new StringBuilder();
		
		for(Position obj : objs){
			String nameStr = generateObjectName(obj);
			switch(obj.getType()){
			case DOOR:
				doors.append(' ');
				doors.append(nameStr);
				break;
			default:
				floors.append(' ');
				floors.append(nameStr);
				break;
			}
		}
		
		ret.append(generateDeclarationLine(doors.toString(), STR_TYPE_DOOR));
		ret.append(generateDeclarationLine(floors.toString(), STR_TYPE_FLOOR));
		
		return ret.toString();
	}
	
	
	/**
	 * Generates the room declarations for the objects tag.
	 * 
	 * @param objs rooms as a list
	 * @return string of room object declarations with room type.
	 */
	protected static String generateRoomList(List<Room> objs) {
		StringBuilder ret = new StringBuilder();
		
		StringBuilder rooms = new StringBuilder();
		
		for(Room obj : objs){
			String nameStr = generateObjectName(obj);
			rooms.append(' ');
			rooms.append(nameStr);
		}
		
		ret.append(generateDeclarationLine(rooms.toString(), STR_TYPE_ROOM));
		
		return ret.toString();
	}
	
	
	/**
	 * Generates the room declarations for the objects tag.
	 * 
	 * @param ids room ids as a list
	 * @return string of room object declarations with room type.
	 */
	protected static String generateRoomListFromIds(List<Integer> ids) {
		StringBuilder ret = new StringBuilder();
		
		StringBuilder rooms = new StringBuilder();
		
		for(Integer id : ids){
			String nameStr = generateObjectName(id);
			rooms.append(' ');
			rooms.append(nameStr);
		}
		
		ret.append(generateDeclarationLine(rooms.toString(), STR_TYPE_ROOM));
		
		return ret.toString();
	}
	
	
	/**
	 * Generates the item declarations for the objects tag.
	 * 
	 * @param objs items as a list
	 * @return string of item object declarations by types.
	 */
	protected static String generateItemList(List<Item> objs) {
		StringBuilder ret = new StringBuilder();
		
		StringBuilder keys      = new StringBuilder();
		StringBuilder ladders   = new StringBuilder();
		StringBuilder treasures = new StringBuilder();
		
		for(Item obj : objs){
			String nameStr = generateObjectName(obj);
			switch(obj.getType()){
			case KEY:
				keys.append(' ');
				keys.append(nameStr);
				break;
			case LADDER:
				ladders.append(' ');
				ladders.append(nameStr);
				break;
			case TREASURE:
				treasures.append(' ');
				treasures.append(nameStr);
				break;
			}
		}
		
		ret.append(generateDeclarationLine(keys.toString(), STR_TYPE_KEY));
//		ret.append(generateDeclarationLine(ladders.toString(), STR_TYPE_LADDER));
		ret.append(generateDeclarationLine(treasures.toString(), STR_TYPE_TREASURE));
		
		return ret.toString();
	}
	
	
	/**
	 * Generates the agent declarations for the objects tag.
	 * 
	 * @param objs agents as a list
	 * @return string of agent object declarations by types.
	 */
	protected static String generateAgentList(List<Agent> objs) {
		StringBuilder ret = new StringBuilder();
		
		StringBuilder burglars = new StringBuilder();
		StringBuilder dogs     = new StringBuilder();
		StringBuilder guards   = new StringBuilder();
		
		for(Agent obj : objs){
			String nameStr = generateObjectName(obj.getId());
			switch(obj.getType()){
			case BURGLAR:
				burglars.append(' ');
				burglars.append(nameStr);
				break;
			case DOG:
				dogs.append(' ');
				dogs.append(nameStr);
				break;
			case GUARD:
				guards.append(' ');
				guards.append(nameStr);
				break;
			}
		}
		
		ret.append(generateDeclarationLine(burglars.toString(), STR_TYPE_BURGLAR));
		ret.append(generateDeclarationLine(dogs.toString(), STR_TYPE_DOG));
		ret.append(generateDeclarationLine(guards.toString(), STR_TYPE_GUARD));
		
		return ret.toString();
	}
	
	
	// -------------------------------------------------------------------------
	
	
	/**
	 * Generates a declaration line (objects with one type).
	 * 
	 * @param entities entities as a string
	 * @param typeName the type of the entities
	 * @return generated line
	 */
	private static String generateDeclarationLine(
			String entities,
			String typeName
	) {
		StringBuilder builder = new StringBuilder();
		builder.append('\t');
		builder.append('\t');
		if(entities.length() > 0){
			builder.append(entities);
		}else{
			builder.append(' ');
			builder.append(generateDummyName(typeName));
			
		}
		builder.append(' ');
		builder.append('-');
		builder.append(' ');
		builder.append(typeName);
		builder.append('\n');
		return builder.toString();
	}
	
	
	/**
	 * 
	 * @param id
	 * @return
	 */
	protected static String generateObjectName(int id) {
		return "o" + id;
	}
	
	
	/**
	 * 
	 * @param object
	 * @return
	 */
	protected static String generateObjectName(BaseObject object) {
		return PDDLBase.generateObjectName(object.getId());
	}
	
	
	/**
	 * 
	 * @param pos
	 * @return
	 */
	protected static String generateObjectName(Position pos) {
		return PDDLBase.generateObjectName((BaseObject)pos);
	}
	
	
	/**
	 * 
	 * @param item
	 * @return
	 */
	protected static String generateObjectName(Item item) {
		return PDDLBase.generateObjectName((BaseObject)item);
	}
	
	
	// -------------------------------------------------------------------------
	
	
	/**
	 * 
	 * @param typeName
	 * @return
	 */
	protected static String generateDummyName(String typeName) {
		return STR_DUMMY + '_' + typeName;
	}
	
	
	// -------------------------------------------------------------------------
	
	
	/**
	 * 
	 */
	public static void writeToFile(String text, String filename) {
		// write to the file:
		try{
			// Create file 
			FileWriter fstream = new FileWriter(filename);
			BufferedWriter out = new BufferedWriter(fstream);
			out.write(text);
			// Close the output stream
			out.close();
		}catch(Exception e){	// Catch exception if any
			System.err.println("Error: " + e.getMessage());
		}
	}
	
}
